/**
TODO:
    1. Fill in description for the file.
    2. remove unnecessary commented lines.
    3. Resolve all TODO's.
    4. Add comments for functions.
  */
#include "globals.h"
//#define FD_SETSIZE 256

uint32_t seq;

int main()
{
	FILE * fp;
	
	const int on = 1;
	int server_port = 0;
    	int i = 1;
	int j;
    	int ele_count = 0;
    	int max;
    	int err;
    	int confd; 
    	int flags;
    	int client_port;
   	uint32_t win = 0; 	

	char buf[TMP_BUFSIZE] = "\0";
    	char filename[TMP_BUFSIZE] = "\0";
	char ip_client[INET_ADDRSTRLEN];
    	char ip_server[INET_ADDRSTRLEN];
    	char *ptr;
	
    	struct ifi_info	*ifi;
    	struct ifi_info *ifihead;	

	struct sockaddr_in *sa_in;
    	struct sockaddr_in cliaddr;
    	struct sockaddr_in servaddr;
    	struct sockaddr * sa;
	struct sockaddr_in sock_nm;
	
	struct sock_ele s[TMP_BUFSIZE];
	
	struct packet pk;

    	socklen_t len;
	
    	fd_set rset;

	srand(time(NULL));
	seq = rand()%100000;

    parse_server_in(&server_port, &win);

	for (ifihead = ifi = Get_ifi_info_plus(AF_INET, 1), i=0; ifi != NULL; ifi = ifi->ifi_next, i++) 
	{
		s[i].sockfd = Socket(AF_INET, SOCK_DGRAM, 0);

		Setsockopt(s[i].sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

		sa_in = (struct sockaddr_in *) ifi->ifi_addr;
		sa_in->sin_family = AF_INET;
		sa_in->sin_port = htons(server_port);
		Bind(s[i].sockfd, (SA *) sa_in, sizeof(*sa_in));
		strcpy(s[i].ip_addr,Sock_ntop((SA *) sa_in, sizeof(*sa_in)));
		sa =  ifi->ifi_ntmaddr;
		strcpy(s[i].net_mask,Sock_ntop_host(sa, sizeof(*sa)));

        get_subnet_mask(s[i].ip_addr, s[i].net_mask, s[i].sub_mask);
        
        printf("%s Interface %d\n\tIP Address :: %s\n\tNetwork Mask :: %s\n\tSubnet Mask :: %s\n\n",\ 
                SRV_LOG, (i + 1), s[i].ip_addr, s[i].net_mask, s[i].sub_mask);
	}

	free_ifi_info_plus(ifihead);
	
	ele_count = i;

	FD_ZERO(&rset);		

    //
    // Used to handle the SIGCHILD signal
    //

	Signal(SIGCHLD,sig_chld); 	

    while (TRUE)
	{
    	max = 0;
	
		for (i = 0; i < ele_count; i++)
		{	
			FD_SET(s[i].sockfd, &rset);
			
            if(max<s[i].sockfd)
            {
			    max=s[i].sockfd;
            }
		}
		
		bzero(buf, TMP_BUFSIZE);	
	    log_msg_int("B4 Select", getpid());

       	err = select(max + 1, &rset, NULL, NULL, NULL);

        if (err < 0 && errno == EINTR)
		{
			log_msg("EINTR error");
			continue;
		}
		else if (err < 0)
        {
			err_sys("Select Error %d\n", errno);
        }

		log_msg_int("After Select", getpid());
	 
        flags = 0;

        for (i = 0; i < ele_count; i++)
        {
            if (FD_ISSET(s[i].sockfd, &rset))
            {
                len = sizeof(cliaddr);	

                log_msg_int_int("B4 Recv from", getpid(), s[i].sockfd);
                
                recvfrom(s[i].sockfd, &pk, sizeof(pk), flags, (SA*) &cliaddr, &len);
		printf( "%s Packet received from Client [ SEQ: %d , ACK: %d ] :: Request for file '%s' \n", SRV_LOG, pk.seqno , pk.ackno, pk.data);
//		pk = get_packet(pk, "", 0);
//	        Sendto(s[i].sockfd, &pk, sizeof(pk), flags, (SA*) &cliaddr, len);				
		printf( "%s Packet sent to Client [ SEQ: %d , ACK: %d ]\n", SRV_LOG, pk.seqno , pk.ackno);
		            
                strcpy(filename, pk.data);
                strncpy(ip_client, Sock_ntop((SA*) &cliaddr, len), INET_ADDRSTRLEN);

                ptr = strchr(ip_client, ':');
                *ptr='\0';
                ptr = strchr(Sock_ntop((SA*)&cliaddr, len), ':');

                client_port = atoi((ptr + 1));
                strncpy(ip_server, s[i].ip_addr, INET_ADDRSTRLEN);

                ptr=strchr(ip_server, ':');
                *ptr='\0';	
                break;
            }	
        }	
    
        if (fork() == 0)
        {
            flags = 0;

            for (j = 0; j < ele_count; j++)
            {
                if (j != i)
                {
                    close(s[j].sockfd);
                }
            }
        
            if (strcmp(ip_server, "127.0.0.1") == 0)
            {
                printf("%s Client and Server are on the same host\n\tIPclient::%s IPserver :: %s\n", 
                        SRV_LOG, ip_client, ip_server);
                flags = MSG_DONTROUTE;
            }
            else 
            {
                //
                // Check whether client is local
                //
                
                char check_sub_msk[INET_ADDRSTRLEN] = "\0";
        
                get_subnet_mask(ip_client, s[i].net_mask, check_sub_msk);
                
                if (strcmp(check_sub_msk, s[i].sub_mask) == 0)
                {
                    printf("%s Client and Server are local i.e. in the same subnet\n\tIPclient::%s IPserver::%s\n", 
                            SRV_LOG, ip_client, ip_server);
                    break;
                }
                else
                {
                    printf("%s Client and Server are not local\n\tIPclient::%s IPserver::%s\n", 
                            SRV_LOG, ip_client, ip_server);
                }								
            }
            
            confd = Socket(AF_INET,SOCK_DGRAM, 0);

            bzero(&servaddr, sizeof(servaddr));
            servaddr.sin_family = AF_INET;
            inet_pton(AF_INET, ip_server, &servaddr.sin_addr);
            servaddr.sin_port = htons(0);
            Bind(confd, (SA *) & servaddr, sizeof(servaddr));
            
            len = sizeof(sock_nm);
            getsockname(confd, (SA*) &sock_nm, &len);
            sprintf(buf,"%s%s", Sock_ntop((SA*) &sock_nm, len), "\0");

            printf("%s Server Socket created and Binded\n\tIPServer:Portno: %s\n", 
                    SRV_LOG, buf);
        
            bzero(&cliaddr, sizeof(cliaddr));
            cliaddr.sin_family = AF_INET;
            inet_pton(AF_INET, ip_client, &cliaddr.sin_addr);
            cliaddr.sin_port = htons(client_port);
            Connect(confd, (SA*) &cliaddr, sizeof(cliaddr));

            len = sizeof(cliaddr);

            pk = get_packet(pk, buf, 0); 
            Sendto(s[i].sockfd, &pk, sizeof(pk), flags, (SA*) &cliaddr, len);
	    printf( "%s Packet sent to Client [ SEQ: %d , ACK: %d ] :: Data :-  '%s'\n", SRV_LOG, pk.seqno , pk.ackno, pk.data);
 	    recv(confd, &pk, sizeof(pk), flags);
	    printf( "%s Packet received from Client [ SEQ: %d , ACK: %d ] \n", SRV_LOG, pk.seqno , pk.ackno);            
            
/*
            printf("%s Received buf '%s'\n", SRV_LOG, buf);//ACK frm Client
            
            close(s[i].sockfd);
            
            fp = fopen(filename, "r");
            
            if (fp == NULL)
            {
                strcpy(buf, "Cannot open file :: FILE NOT FOUND");
                send(confd, buf, TMP_BUFSIZE, flags);	
            }
            else
            {
                while (Fgets(buf, 99, fp) != NULL)
                {
                    strcat(buf, "\0");
                    send(confd, buf, TMP_BUFSIZE, flags);
                }

                buf[0] = EOF;
                send(confd, buf, TMP_BUFSIZE, flags);
                fclose(fp);
                close(confd);

                log_msg("BBye child\n");
                exit(0);
           }
            exit(0);*/
        }
        else
        {
            log_msg_int("Parent... ",getpid());
        }	
		
    }
	return(0);
}

/**
TODO: Description.
  */
void 
parse_server_in(
    int *server_port, 
    int *win
    )
{
    FILE * fp;

    int i;
    int j;
    int flags = 0;

    char buf[TMP_BUFSIZE] = "\0";
	
   	if ((fp = fopen("server.in", "r")) == NULL)
	{
		printf("%s server.in not found\n", SRV_LOG);
		exit(1);
	}
	else
	{
        i = 1;

		while (Fgets(buf, TMP_BUFSIZE, fp) != NULL)
		{
			flags = 0;

			for (j = 0; j < strlen(buf) - 1 && isdigit(buf[j]); j++);
			
            if (j != strlen(buf) - 1)
            {
				flags=1;
            }

			switch (i)
			{
				case 1: 
					if(flags || (*server_port = atoi(buf)) == 0)
					{
						printf("%s Error server.in: Line 1 contains invalid Port number\n", SRV_LOG);
						exit(1);
					}	
					
                    i++;
					break;
				case 2:
					if(flags || ( *win = atoi(buf)) == 0)
					{
						printf("%s Error server.in: Line 2 contains invalid Sending Sliding Window Size\n", SRV_LOG);
						exit(1);
					}	
				
                    i++;
					break;
			}	

		    bzero(buf, TMP_BUFSIZE);	
		}
		
        printf("%s Server on Port number %d and Sending Sliding Window Size %d\n",
                SRV_LOG, *server_port, *win);
		fclose(fp);
	}
}

struct packet
get_packet(
	struct packet p,
	char buf[TMP_BUFSIZE], 
	uint32_t  win)
{
	p.ackno = p.seqno+1;
	p.win = win;
	p.seqno = seq;
	strcpy(p.data, buf);
	seq++;
	return p;
}
